<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="Java面试题（Mysql篇）, Angus">
    <meta name="description" content="Java面试Mysql题型相关讲解">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    

    <title>Java面试题（Mysql篇） | Angus</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-3.6.0.min.js"></script>

<meta name="generator" content="Hexo 6.1.0">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="alternate" href="/atom.xml" title="Angus" type="application/atom+xml">
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">Angus</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>留言板</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">Angus</div>
        <div class="logo-desc">
            
            never give up!
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			留言板
		</a>
          
        </li>
        
        
        <li><div class="divider"></div></li>
        <li>
            <a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="waves-effect waves-light" target="_blank">
                <i class="fab fa-github-square fa-fw"></i>Fork Me
            </a>
        </li>
        
    </ul>
</div>


        </div>

        
            <style>
    .nav-transparent .github-corner {
        display: none !important;
    }

    .github-corner {
        position: absolute;
        z-index: 10;
        top: 0;
        right: 0;
        border: 0;
        transform: scale(1.1);
    }

    .github-corner svg {
        color: #0f9d58;
        fill: #fff;
        height: 64px;
        width: 64px;
    }

    .github-corner:hover .octo-arm {
        animation: a 0.56s ease-in-out;
    }

    .github-corner .octo-arm {
        animation: none;
    }

    @keyframes a {
        0%,
        to {
            transform: rotate(0);
        }
        20%,
        60% {
            transform: rotate(-25deg);
        }
        40%,
        80% {
            transform: rotate(10deg);
        }
    }
</style>

<a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="github-corner tooltipped hide-on-med-and-down" target="_blank"
   data-tooltip="Fork Me" data-position="left" data-delay="50">
    <svg viewBox="0 0 250 250" aria-hidden="true">
        <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
        <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
              fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
        <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
              fill="currentColor" class="octo-body"></path>
    </svg>
</a>
        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/23.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">Java面试题（Mysql篇）</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Java/">
                                <span class="chip bg-color">Java</span>
                            </a>
                        
                            <a href="/tags/%E9%9D%A2%E8%AF%95/">
                                <span class="chip bg-color">面试</span>
                            </a>
                        
                            <a href="/tags/Mysql/">
                                <span class="chip bg-color">Mysql</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                面试宝典
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2022-08-09
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2022-09-11
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    18.6k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    65 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        

        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h1 id="Java面试题（Mysql篇）"><a href="#Java面试题（Mysql篇）" class="headerlink" title="Java面试题（Mysql篇）"></a>Java面试题（Mysql篇）</h1><h2 id="一、索引相关"><a href="#一、索引相关" class="headerlink" title="一、索引相关"></a>一、索引相关</h2><h3 id="1-如何创建索引？"><a href="#1-如何创建索引？" class="headerlink" title="1. 如何创建索引？"></a>1. 如何创建索引？</h3><p>创建索引有三种方式。</p>
<p>1、 在执行CREATE TABLE时创建索引</p>
<pre class=" language-sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> user_index2 <span class="token punctuation">(</span>
    id <span class="token keyword">INT</span> <span class="token keyword">auto_increment</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>
    first_name <span class="token keyword">VARCHAR</span> <span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    last_name <span class="token keyword">VARCHAR</span> <span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    id_card <span class="token keyword">VARCHAR</span> <span class="token punctuation">(</span><span class="token number">18</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    information <span class="token keyword">text</span><span class="token punctuation">,</span>
    <span class="token keyword">KEY</span> name <span class="token punctuation">(</span>first_name<span class="token punctuation">,</span> last_name<span class="token punctuation">)</span><span class="token punctuation">,</span>
    FULLTEXT <span class="token keyword">KEY</span> <span class="token punctuation">(</span>information<span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">UNIQUE</span> <span class="token keyword">KEY</span> <span class="token punctuation">(</span>id_card<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>2、 使用ALTER TABLE命令去增加索引。</p>
<pre class=" language-sql"><code class="language-sql"><span class="token keyword">ALTER</span> <span class="token keyword">TABLE</span> table_name <span class="token keyword">ADD</span> <span class="token keyword">INDEX</span> index_name <span class="token punctuation">(</span>column_list<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。</p>
<p>其中table_name是要增加索引的表名，column_list指出对哪些列进行索引，多列时各列之间用逗号分隔。</p>
<p>索引名index_name可自己命名，缺省时，MySQL将根据第一个索引列赋一个名称。另外，ALTER TABLE允许在单个语句中更改多个表，因此可以在同时创建多个索引。 3、 使用CREATE INDEX命令创建。</p>
<pre class=" language-sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">INDEX</span> index_name <span class="token keyword">ON</span> table_name <span class="token punctuation">(</span>column_list<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<h3 id="2-创建索引时需要注意什么？"><a href="#2-创建索引时需要注意什么？" class="headerlink" title="2. 创建索引时需要注意什么？"></a>2. 创建索引时需要注意什么？</h3><ul>
<li>非空字段：应该指定列为NOT NULL，除非你想存储NULL。在mysql中，含有空值的列很难进行查询优化，因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值；</li>
<li>取值离散大的字段：（变量各个取值之间的差异程度）的列放到联合索引的前面，可以通过count()函数查看字段的差异值，返回值越大说明字段的唯一值越多字段的离散程度高；</li>
<li>索引字段越小越好：数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。</li>
</ul>
<h3 id="1、索引的基本原理"><a href="#1、索引的基本原理" class="headerlink" title="1、索引的基本原理"></a>1、索引的基本原理</h3><p>索引⽤来快速地寻找那些具有特定值的记录。如果没有索引，⼀般来说执⾏查询时遍历整张表。 </p>
<p>索引的原理：就是把⽆序的数据变成有序的查询 </p>
<ol>
<li><p>把创建了索引的列的内容进⾏排序 </p>
</li>
<li><p>对排序结果⽣成倒排表 </p>
</li>
<li><p>在倒排表内容上拼上数据地址链 </p>
</li>
<li><p>在查询的时候，先拿到倒排表内容，再取出数据地址链，从⽽拿到具体数据</p>
</li>
</ol>
<h3 id="2、Mysql聚簇和⾮聚簇索引的区别"><a href="#2、Mysql聚簇和⾮聚簇索引的区别" class="headerlink" title="2、Mysql聚簇和⾮聚簇索引的区别"></a>2、Mysql聚簇和⾮聚簇索引的区别</h3><p>都是B+树的数据结构 </p>
<ul>
<li>聚簇索引：将数据存储与索引放到了⼀块、并且是按照⼀定的顺序组织的，找到索引也就找到了数 据，数据的物理存放顺序与索引顺序是⼀致的，即：只要索引是相邻的，那么对应的数据⼀定也是 相邻地存放在磁盘上的</li>
</ul>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220909103041754.png" alt="image-20220909103041754"></p>
<ul>
<li>⾮聚簇索引：叶⼦节点不存储数据、存储的是数据⾏地址，也就是说根据索引查找到数据⾏的位置 再取磁盘查找数据，这个就有点类似⼀本树的⽬录，⽐如我们要找第三章第⼀节，那我们先在这个 ⽬录⾥⾯找，找到对应的⻚码后再去对应的⻚码看⽂章。</li>
</ul>
<p><strong>优势：</strong>1、查询通过聚簇索引可以直接获取数据，相⽐⾮聚簇索引需要第⼆次查询（⾮覆盖索引的情况 下）效率要⾼ </p>
<p>2、聚簇索引对于范围查询的效率很⾼，因为其数据是按照⼤⼩排列的 </p>
<p>3、聚簇索引适合⽤在排序的场合，⾮聚簇索引不适合 </p>
<p><strong>劣势：</strong> </p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220909103720337.png" alt="image-20220909103720337"></p>
<p>1、维护索引很昂贵，特别是插⼊新⾏或者主键被更新导⾄要分⻚(page split)的时候。建议在⼤量插⼊ 新⾏后，选在负载较低的时间段，通过OPTIMIZE TABLE优化表，因为必须被移动的⾏数据可能造成碎 ⽚。使⽤独享表空间可以弱化碎⽚ </p>
<p>2、表因为使⽤UUId（随机ID）作为主键，使数据存储稀疏，这就会出现聚簇索引有可能有⽐全表扫⾯ 更慢，所以建议使⽤int的auto_increment作为主键 </p>
<p>3、如果主键⽐较⼤的话，那辅助索引将会变的更⼤，因为辅助索引的叶⼦存储的是主键值；过⻓的主 键值，会导致⾮叶⼦节点占⽤占⽤更多的物理空间nnoDB中⼀定有主键，主键⼀定是聚簇索引，不⼿动设置、则会使⽤unique索引，没有unique索引，则会使⽤数据库内部的⼀个⾏的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引，辅助索引访问数据总是需要⼆次查找，⾮聚簇索引都是辅助索引，像复合索引、前缀索引、唯⼀索引， 辅助索引叶⼦节点存储的不再是⾏的物理位置，⽽是主键值 </p>
<p>MyISM使⽤的是⾮聚簇索引，没有聚簇索引，⾮聚簇索引的两棵B+树看上去没什么不同，节点的结构完 全⼀致只是存储的内容不同⽽已，主键索引B+树的节点存储了主键，辅助键索引B+树存储了辅助键。 表数据存储在独⽴的地⽅，这两颗B+树的叶⼦节点都使⽤⼀个地址指向真正的表数据，对于表数据来 说，这两个键没有任何差别。由于索引树是独⽴的，通过辅助键检索⽆需访问主键的索引树。 如果涉及到⼤数据量的排序、全表扫描、count之类的操作的话，还是MyISAM占优势些，因为索引所占空间⼩，这些操作是需要在内存中完成的。 </p>
<h3 id="3、Mysql索引的数据结构，各⾃优劣"><a href="#3、Mysql索引的数据结构，各⾃优劣" class="headerlink" title="3、Mysql索引的数据结构，各⾃优劣"></a>3、Mysql索引的数据结构，各⾃优劣</h3><p>索引的数据结构和具体存储引擎的实现有关，在MySQL中使⽤较多的索引有Hash索引，B+树索引等， InnoDB存储引擎的默认索引实现为：B+树索引。对于哈希索引来说，底层的数据结构就是哈希表，因 此在绝⼤多数需求为单条记录查询的时候，可以选择哈希索引，查询性能最快；其余⼤部分场景，建议 选择BTree索引。 </p>
<p><strong>B+树：</strong>B+树是⼀个平衡的多叉树，从根节点到每个叶⼦节点的⾼度差值不超过1，⽽且同层级的节点间 有指针相互链接。在B+树上的常规检索，从根节点到叶⼦节点的搜索效率基本相当，不会出现⼤幅波 动，⽽且基于索引的顺序扫描时，也可以利⽤双向指针快速左右移动，效率⾮常⾼。因此，B+树索引被 ⼴泛应⽤于数据库、⽂件系统等场景。 </p>
<p><strong>哈希索引：</strong>哈希索引就是采⽤⼀定的哈希算法，把键值换算成新的哈希值，检索时不需要类似B+树那样 从根节点到叶⼦节点逐级查找，只需⼀次哈希算法即可⽴刻定位到相应的位置，速度⾮常快 </p>
<p>如果是等值查询，那么哈希索引明显有绝对优势，因为只需要经过⼀次算法即可找到相应的键值；前提 是键值都是唯⼀的。如果键值不是唯⼀的，就需要先找到该键所在位置，然后再根据链表往后扫描，直 到找到相应的数据； </p>
<p>如果是范围查询检索，这时候哈希索引就毫⽆⽤武之地了，因为原先是有序的键值，经过哈希算法后， 有可能变成不连续的了，就没办法再利⽤索引完成范围查询检索；</p>
<p>哈希索引也没办法利⽤索引完成排序，以及like ‘xxx%’ 这样的部分模糊查询（这种部分模糊查询，其实 本质上也是范围查询）； </p>
<p>哈希索引也不⽀持多列联合索引的最左匹配规则； </p>
<p>B+树索引的关键字检索效率⽐较平均，不像B树那样波动幅度⼤，在有⼤量重复键值情况下，哈希索引 的效率也是极低的，因为存在哈希碰撞问题。 </p>
<h3 id="4、索引设计的原则？"><a href="#4、索引设计的原则？" class="headerlink" title="4、索引设计的原则？"></a>4、索引设计的原则？</h3><p>查询更快、占⽤空间更⼩ </p>
<ol>
<li><p>适合索引的列是出现在where⼦句中的列，或者连接⼦句中指定的列 </p>
</li>
<li><p>基数较⼩的表，索引效果较差，没有必要在此列建⽴索引 </p>
</li>
<li><p>使⽤短索引，如果对⻓字符串列进⾏索引，应该指定⼀个前缀⻓度，这样能够节省⼤量索引空间， 如果搜索词超过索引前缀⻓度，则使⽤索引排除不匹配的⾏，然后检查其余⾏是否可能匹配。 </p>
</li>
<li><p>不要过度索引。索引需要额外的磁盘空间，并降低写操作的性能。在修改表内容的时候，索引会进 ⾏更新甚⾄重构，索引列越多，这个时间就会越⻓。所以只保持需要的索引有利于查询即可。 </p>
</li>
<li><p>定义有外键的数据列⼀定要建⽴索引。 </p>
</li>
<li><p>更新频繁字段不适合创建索引 </p>
</li>
<li><p>若是不能有效区分数据的列不适合做索引列(如性别，男⼥未知，最多也就三种，区分度实在太低) </p>
</li>
<li><p>尽量的扩展索引，不要新建索引。⽐如表中已经有a的索引，现在要加(a,b)的索引，那么只需要修 改原来的索引即可。 </p>
</li>
<li><p>对于那些查询中很少涉及的列，重复值⽐较多的列不要建⽴索引。 </p>
</li>
<li><p>对于定义为text、image和bit的数据类型的列不要建⽴索引。</p>
</li>
</ol>
<h3 id="5、简述Mysql中索引类型及对数据库的性能的影响"><a href="#5、简述Mysql中索引类型及对数据库的性能的影响" class="headerlink" title="5、简述Mysql中索引类型及对数据库的性能的影响"></a>5、简述Mysql中索引类型及对数据库的性能的影响</h3><p>普通索引：允许被索引的数据列包含重复的值。 </p>
<p>唯⼀索引：可以保证数据记录的唯⼀性。 </p>
<p>主键：是⼀种特殊的唯⼀索引，在⼀张表中只能定义⼀个主键索引，主键⽤于唯⼀标识⼀条记录，使⽤ 关键字 PRIMARY KEY 来创建。 </p>
<p>联合索引：索引可以覆盖多个数据列，如像INDEX(columnA, columnB)索引。 </p>
<p>全⽂索引：通过建⽴ 倒排索引 ,可以极⼤的提升检索效率,解决判断字段是否包含的问题，是⽬前搜索引 擎使⽤的⼀种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全⽂索引</p>
<p>索引可以极⼤的提⾼数据的查询速度</p>
<p>通过使⽤索引，可以在查询的过程中，使⽤优化隐藏器，提⾼系统的性能。 </p>
<p>但是会降低插⼊、删除、更新表的速度，因为在执⾏这些写操作时，还要操作索引⽂件 </p>
<p>索引需要占物理空间，除了数据表占数据空间之外，每⼀个索引还要占⼀定的物理空间，如果要建⽴聚 簇索引，那么需要的空间就会更⼤，如果⾮聚集索引很多，⼀旦聚集索引改变，那么所有⾮聚集索引都 会跟着变。 </p>
<h3 id="6、索引覆盖是什么"><a href="#6、索引覆盖是什么" class="headerlink" title="6、索引覆盖是什么"></a>6、索引覆盖是什么</h3><p>索引覆盖就是⼀个SQL在执⾏时，可以利⽤索引来快速查找，并且此SQL所要查询的字段在当前索引对 应的字段中都包含了，那么就表示此SQL⾛完索引后不⽤回表了，所需要的字段都在当前索引的叶⼦节点上存在，可以直接作为结果返回了 </p>
<h3 id="7、最左前缀原则是什么"><a href="#7、最左前缀原则是什么" class="headerlink" title="7、最左前缀原则是什么"></a>7、最左前缀原则是什么</h3><h4 id="前缀索引"><a href="#前缀索引" class="headerlink" title="前缀索引"></a>前缀索引</h4><p>因为可能我们索引的字段非常长，这既占内存空间，也不利于维护。所以我们就想，如果只把很长字段的前面的公共部分作为一个索引，就会产生超级加倍的效果。但是，我们需要注意，order by不支持前缀索引 。</p>
<p>流程是：</p>
<p>先计算完整列的选择性 :<code>select count(distinct col_1)/count(1) from table_1</code></p>
<p>再计算不同前缀长度的选择性 :<code>select count(distinct left(col_1,4))/count(1) from table_1 </code></p>
<p>找到最优长度之后，创建前缀索引 :<code> create index idx_front on table_1 (col_1(4))</code></p>
<h4 id="最左前缀原则"><a href="#最左前缀原则" class="headerlink" title="最左前缀原则"></a>最左前缀原则</h4><p>当⼀个SQL想要利⽤索引是，就⼀定要提供该索引所对应的字段中最左边的字段，也就是排在最前⾯的 字段，⽐如针对a,b,c三个字段建⽴了⼀个联合索引，那么在写⼀个sql时就⼀定要提供a字段的条件，这样才能⽤到联合索引，这是由于在建⽴a,b,c三个字段的联合索引时，底层的B+树是按照a,b,c三个字段从左往右去⽐较⼤⼩进⾏排序的，所以如果想要利⽤B+树进⾏快速查找也得符合这个规则 </p>
<h3 id="8-MySQL有哪几种索引类型？"><a href="#8-MySQL有哪几种索引类型？" class="headerlink" title="8. MySQL有哪几种索引类型？"></a>8. MySQL有哪几种索引类型？</h3><p>1、从存储结构上来划分：BTree索引（B-Tree或B+Tree索引），Hash索引，full-index全文索引，R-Tree索引。这里所描述的是索引存储时保存的形式，</p>
<p>2、从应用层次来分：普通索引，唯一索引，复合索引。</p>
<ul>
<li>普通索引：即一个索引只包含单个列，一个表可以有多个单列索引</li>
<li>唯一索引：索引列的值必须唯一，但允许有空值</li>
<li>复合索引：多列值组成一个索引，专门用于组合搜索，其效率大于索引合并</li>
<li>聚簇索引(聚集索引)：并不是一种单独的索引类型，而是一种数据存储方式。具体细节取决于不同的实现，InnoDB的聚簇索引其实就是在同一个结构中保存了B-Tree索引(技术上来说是B+Tree)和数据行。</li>
<li>非聚簇索引： 不是聚簇索引，就是非聚簇索引</li>
</ul>
<p>3、根据中数据的物理顺序与键值的逻辑（索引）顺序关系： 聚集索引，非聚集索引。</p>
<h3 id="9-说一说索引的底层实现？"><a href="#9-说一说索引的底层实现？" class="headerlink" title="9. 说一说索引的底层实现？"></a>9. 说一说索引的底层实现？</h3><p><strong>Hash索引</strong></p>
<p>基于哈希表实现，只有精确匹配索引所有列的查询才有效，对于每一行数据，存储引擎都会对所有的索引列计算一个哈希码（hash code），并且Hash索引将所有的哈希码存储在索引中，同时在索引表中保存指向每个数据行的指针。</p>
<blockquote>
<p>图片来源：<a target="_blank" rel="noopener" href="https://www.javazhiyin.com/40232.html">https://www.javazhiyin.com/40232.html</a></p>
</blockquote>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210411215012443.png" alt="img"></p>
<p><strong>B-Tree索引</strong>（MySQL使用B+Tree）</p>
<p>B-Tree能加快数据的访问速度，因为存储引擎不再需要进行全表扫描来获取数据，数据分布在各个节点之中。</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210411215023820.png" alt="img"></p>
<p><strong>B+Tree索引</strong></p>
<p>是B-Tree的改进版本，同时也是数据库索引索引所采用的存储结构。数据都在叶子节点上，并且增加了顺序访问指针，每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说，进行范围查找时只需要查找两个节点，进行遍历即可。而B-Tree需要获取所有节点，相比之下B+Tree效率更高。</p>
<p>B+tree性质：</p>
<ul>
<li>n棵子tree的节点包含n个关键字，不用来保存数据而是保存数据的索引。</li>
<li>所有的叶子结点中包含了全部关键字的信息，及指向含这些关键字记录的指针，且叶子结点本身依关键字的大小自小而大顺序链接。</li>
<li>所有的非终端结点可以看成是索引部分，结点中仅含其子树中的最大（或最小）关键字。</li>
<li>B+ 树中，数据对象的插入和删除仅在叶节点上进行。</li>
<li>B+树有2个头指针，一个是树的根节点，一个是最小关键码的叶节点。</li>
</ul>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210411215044332.png" alt="img"></p>
<h3 id="10-为什么索引结构默认使用B-Tree，而不是B-Tree，Hash，二叉树，红黑树？"><a href="#10-为什么索引结构默认使用B-Tree，而不是B-Tree，Hash，二叉树，红黑树？" class="headerlink" title="10. 为什么索引结构默认使用B+Tree，而不是B-Tree，Hash，二叉树，红黑树？"></a>10. 为什么索引结构默认使用B+Tree，而不是B-Tree，Hash，二叉树，红黑树？</h3><p>B-tree： 从两个方面来回答</p>
<ul>
<li>B+树的磁盘读写代价更低：B+树的内部节点并没有指向关键字具体信息的指针，因此其内部节点相对B(B-)树更小，如果把所有同一内部节点的关键字存放在同一盘块中，那么盘块所能容纳的关键字数量也越多，一次性读入内存的需要查找的关键字也就越多，相对<code>IO读写次数就降低</code>了。</li>
<li>由于B+树的数据都存储在叶子结点中，分支结点均为索引，方便扫库，只需要扫一遍叶子结点即可，但是B树因为其分支结点同样存储着数据，我们要找到具体的数据，需要进行一次中序遍历按序来扫，所以B+树更加适合在<code>区间查询</code>的情况，所以通常B+树用于数据库索引。</li>
</ul>
<p>Hash：</p>
<ul>
<li><p>虽然可以快速定位，但是没有顺序，IO复杂度高；</p>
</li>
<li><p>基于Hash表实现，只有Memory存储引擎显式支持哈希索引 ；</p>
</li>
<li><p>适合<strong>等值查询</strong>，如=、in()、&lt;=&gt;，不支持范围查询 ；</p>
</li>
<li><p>因为不是按照索引值顺序存储的，就不能像B+Tree索引一样利用索引完成<a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/">排序</a> ；</p>
</li>
<li><p>Hash索引在查询等值时非常快 ；</p>
</li>
<li><p>因为Hash索引始终索引的<strong>所有列的全部内容</strong>，所以不支持部分索引列的匹配查找 ；</p>
</li>
<li><p>如果有大量重复键值得情况下，哈希索引的效率会很低，因为存在哈希碰撞问题 。</p>
</li>
</ul>
<p>二叉树： 树的高度不均匀，不能自平衡，查找效率跟数据有关（树的高度），并且IO代价高。</p>
<p>红黑树： 树的高度随着数据量增加而增加，IO代价高。</p>
<h3 id="11-联合索引是什么？为什么需要注意联合索引中的顺序？"><a href="#11-联合索引是什么？为什么需要注意联合索引中的顺序？" class="headerlink" title="11. 联合索引是什么？为什么需要注意联合索引中的顺序？"></a>11. 联合索引是什么？为什么需要注意联合索引中的顺序？</h3><p>MySQL可以使用多个字段同时建立一个索引，叫做联合索引。在联合索引中，如果想要命中索引，需要按照建立索引时的字段顺序挨个使用，否则无法命中索引。</p>
<p>具体原因为:</p>
<p>MySQL使用索引时需要索引有序，假设现在建立了”name，age，school”的联合索引，那么索引的排序为: 先按照name排序，如果name相同，则按照age排序，如果age的值也相等，则按照school进行排序。</p>
<p>当进行查询时，此时索引仅仅按照name严格有序，因此必须首先使用name字段进行等值查询，之后对于匹配到的列而言，其按照age字段严格有序，此时可以使用age字段用做索引查找，以此类推。因此在建立联合索引的时候应该注意索引列的顺序，一般情况下，将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。</p>
<h3 id="12-怎么查看MySQL语句有没有用到索引？"><a href="#12-怎么查看MySQL语句有没有用到索引？" class="headerlink" title="12. 怎么查看MySQL语句有没有用到索引？"></a>12. 怎么查看MySQL语句有没有用到索引？</h3><p>通过explain，如以下例子：</p>
<pre><code>EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title='Senior Engineer' AND from_date='1986-06-26';
</code></pre>
<table>
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>partitions</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>filtered</th>
<th>rows</th>
<th>Extra</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>SIMPLE</td>
<td>titles</td>
<td>null</td>
<td>const</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>59</td>
<td>const,const,const</td>
<td>10</td>
<td>1</td>
<td></td>
</tr>
</tbody></table>
<ul>
<li><p>id：在⼀个⼤的查询语句中每个<strong>SELECT</strong>关键字都对应⼀个唯⼀的id ，如explain select * from s1 where id = (select id from s1 where name = ‘egon1’);第一个select的id是1，第二个select的id是2。有时候会出现两个select，但是id却都是1，这是因为优化器把子查询变成了连接查询 。</p>
</li>
<li><p>select_type：select关键字对应的那个查询的类型，如SIMPLE,PRIMARY,SUBQUERY,DEPENDENT,SNION 。</p>
</li>
<li><p>table：每个查询对应的表名 。</p>
</li>
<li><p>type：<code>type</code> 字段比较重要, 它提供了判断查询是否高效的重要依据依据. 通过 <code>type</code> 字段, 我们判断此次查询是 <code>全表扫描</code> 还是 <code>索引扫描</code> 等。如const(主键索引或者唯一二级索引进行等值匹配的情况下),ref(普通的⼆级索引列与常量进⾏等值匹配),index(扫描全表索引的覆盖索引) 。</p>
<p>通常来说, 不同的 type 类型的性能关系如下: <code>ALL &lt; index &lt; range ~ index_merge &lt; ref &lt; eq_ref &lt; const &lt; system</code> <code>ALL</code> 类型因为是全表扫描, 因此在相同的查询条件下, 它是速度最慢的. 而 <code>index</code> 类型的查询虽然不是全表扫描, 但是它扫描了所有的索引, 因此比 ALL 类型的稍快.</p>
</li>
<li><p>possible_key：查询中可能用到的索引*(可以把用不到的删掉，降低优化器的优化时间)* 。</p>
</li>
<li><p>key：此字段是 MySQL 在当前查询时所真正使用到的索引。</p>
</li>
<li><p>filtered：查询器预测满足下一次查询条件的百分比 。</p>
</li>
<li><p>rows 也是一个重要的字段. MySQL 查询优化器根据统计信息, 估算 SQL 要查找到结果集需要扫描读取的数据行数. 这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好。</p>
</li>
<li><p>extra：表示额外信息，如Using where,Start temporary,End temporary,Using temporary等。</p>
</li>
</ul>
<h3 id="13-为什么官方建议使用自增长主键作为索引？"><a href="#13-为什么官方建议使用自增长主键作为索引？" class="headerlink" title="13. 为什么官方建议使用自增长主键作为索引？"></a>13. 为什么官方建议使用自增长主键作为索引？</h3><p>结合B+Tree的特点，自增主键是连续的，在插入过程中尽量减少页分裂，即使要进行页分裂，也只会分裂很少一部分。并且能减少数据的移动，每次插入都是插入到最后。总之就是减少分裂和移动的频率。</p>
<h3 id="14-使用索引查询一定能提高查询的性能吗？"><a href="#14-使用索引查询一定能提高查询的性能吗？" class="headerlink" title="14. 使用索引查询一定能提高查询的性能吗？"></a>14. 使用索引查询一定能提高查询的性能吗？</h3><p>通常通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。</p>
<p>索引需要空间来存储，也需要定期维护， 每当有记录在表中增减或索引列被修改时，索引本身也会被修改。 这意味着每条记录的I* NSERT，DELETE，UPDATE将为此多付出4，5 次的磁盘I/O。 因为索引需要额外的存储空间和处理，那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能，索引范围查询(INDEX RANGE SCAN)适用于两种情况:</p>
<ul>
<li>基于一个范围的检索，一般查询返回结果集小于表中记录数的30%。</li>
<li>基于非唯一性索引的检索。</li>
</ul>
<h3 id="15-什么情况下不走索引（索引失效）？"><a href="#15-什么情况下不走索引（索引失效）？" class="headerlink" title="15. 什么情况下不走索引（索引失效）？"></a>15. 什么情况下不走索引（索引失效）？</h3><h5 id="1、使用-x3D-或者-lt-gt-导致索引失效"><a href="#1、使用-x3D-或者-lt-gt-导致索引失效" class="headerlink" title="1、使用!= 或者 <> 导致索引失效"></a>1、使用!= 或者 &lt;&gt; 导致索引失效</h5><h5 id="2、类型不一致导致的索引失效"><a href="#2、类型不一致导致的索引失效" class="headerlink" title="2、类型不一致导致的索引失效"></a>2、类型不一致导致的索引失效</h5><h5 id="3、函数导致的索引失效"><a href="#3、函数导致的索引失效" class="headerlink" title="3、函数导致的索引失效"></a>3、函数导致的索引失效</h5><p>如：</p>
<pre><code>SELECT * FROM `user` WHERE DATE(create_time) = '2020-09-03';
</code></pre>
<p>如果使用函数在索引列，这是不走索引的。</p>
<h5 id="4、运算符导致的索引失效"><a href="#4、运算符导致的索引失效" class="headerlink" title="4、运算符导致的索引失效"></a>4、运算符导致的索引失效</h5><pre><code>SELECT * FROM `user` WHERE age - 1 = 20;
</code></pre>
<p>如果你对列进行了（+，-，*，/，!）, 那么都将不会走索引。</p>
<h5 id="5、OR引起的索引失效"><a href="#5、OR引起的索引失效" class="headerlink" title="5、OR引起的索引失效"></a>5、OR引起的索引失效</h5><pre><code>SELECT * FROM `user` WHERE `name` = '张三' OR height = '175';
</code></pre>
<p>OR导致索引是在特定情况下的，并不是所有的OR都是使索引失效，如果OR连接的是同一个字段，那么索引不会失效，反之索引失效。</p>
<h5 id="6、模糊搜索导致的索引失效"><a href="#6、模糊搜索导致的索引失效" class="headerlink" title="6、模糊搜索导致的索引失效"></a>6、模糊搜索导致的索引失效</h5><pre><code>SELECT * FROM `user` WHERE `name` LIKE '%冰';
</code></pre>
<p>当<code>%</code>放在匹配字段前是不走索引的，放在后面才会走索引。</p>
<h5 id="7、NOT-IN、NOT-EXISTS导致索引失效"><a href="#7、NOT-IN、NOT-EXISTS导致索引失效" class="headerlink" title="7、NOT IN、NOT EXISTS导致索引失效"></a>7、NOT IN、NOT EXISTS导致索引失效</h5><p>插入连续的数据：</p>
<h3 id="16、哪些情况不适合创建索引"><a href="#16、哪些情况不适合创建索引" class="headerlink" title="16、哪些情况不适合创建索引"></a>16、哪些情况不适合创建索引</h3><h4 id="（1）在where中用不到的字段，不要设置索引"><a href="#（1）在where中用不到的字段，不要设置索引" class="headerlink" title="（1）在where中用不到的字段，不要设置索引"></a>（1）在where中用不到的字段，不要设置索引</h4><p>WHERE条件（包括GROUP BY、ORDER BY）里用不到的字段不需要创建索引，索引的价值是快速定位，如果起不到定位的字段通常是不需要创建索引的。</p>
<h4 id="（2）数据量小的表最好不要使用索引"><a href="#（2）数据量小的表最好不要使用索引" class="headerlink" title="（2）数据量小的表最好不要使用索引"></a>（2）数据量小的表最好不要使用索引</h4><p>如果表记录太少，比如少于1000个，那么是不需要创建索引的。表记录太少，是否创建索引对查询效率的影响并不大，甚至说，查询花费的时间可能比遍历索引的时间还要短，索引可能不会产生优化效果</p>
<h4 id="（3）有大量重复数据的列上不需要建立索引"><a href="#（3）有大量重复数据的列上不需要建立索引" class="headerlink" title="（3）有大量重复数据的列上不需要建立索引"></a>（3）有大量重复数据的列上不需要建立索引</h4><p>在条件表达式中经常用到的不同值较多的列上建立索引，但字段中如果有大量重复数据，也不用创建索引。比如在学生表的“性别”字段上只有“男”与“女”两个不同值，因此无法建立索引。如果建立索引，不但不会提高查询效率，反而会严重降低数据的更新速度。</p>
<p>举例：要在100万行数据中查找其中的50万行（比如性别为男的数据），一旦创建了索引，你需要先访问50万次索引，然后再访问50万次数据表，这样加起来的开销比不使用索引可能还要大。</p>
<h4 id="（4）避免对经常更新的表创建过多的索引"><a href="#（4）避免对经常更新的表创建过多的索引" class="headerlink" title="（4）避免对经常更新的表创建过多的索引"></a>（4）避免对经常更新的表创建过多的索引</h4><p>频繁更新的字段不一定要创建索引，因为更新数据的时候，也需要更新索引，如果索引太多，在更新索引的时候也会造成负担，从而影响效率。</p>
<h4 id="（5）不建议用无序的值作为索引"><a href="#（5）不建议用无序的值作为索引" class="headerlink" title="（5）不建议用无序的值作为索引"></a>（5）不建议用无序的值作为索引</h4><p>例如身份证、UUID（在索引比较时需要转为ASCII,，并且插入时可能造成页分裂）、MD5、HASH、无序长字符串等</p>
<h4 id="（6）删除不再使用或者很少使用的索引"><a href="#（6）删除不再使用或者很少使用的索引" class="headerlink" title="（6）删除不再使用或者很少使用的索引"></a>（6）删除不再使用或者很少使用的索引</h4><h4 id="（7）不要定义冗余或重复的索引"><a href="#（7）不要定义冗余或重复的索引" class="headerlink" title="（7）不要定义冗余或重复的索引"></a>（7）不要定义冗余或重复的索引</h4><ul>
<li>​</li>
</ul>
<p>	</p>
<h2 id="二、InnoDB存储引擎的锁的算法"><a href="#二、InnoDB存储引擎的锁的算法" class="headerlink" title="二、InnoDB存储引擎的锁的算法"></a>二、InnoDB存储引擎的锁的算法</h2><ul>
<li>Record lock：单个⾏记录上的锁</li>
<li>Gap lock：间隙锁，锁定⼀个范围，不包括记录本身 </li>
<li>Next-key lock：record+gap 锁定⼀个范围，包含记录本身</li>
</ul>
<p>相关知识点： </p>
<ol>
<li><p>innodb对于⾏的查询使⽤next-key lock </p>
</li>
<li><p>Next-locking keying为了解决Phantom Problem幻读问题 </p>
</li>
<li><p>当查询的索引含有唯⼀属性时，将next-key lock降级为record key </p>
</li>
<li><p>Gap锁设计的⽬的是为了阻⽌多个事务将记录插⼊到同⼀范围内，⽽这会导致幻读问题的产⽣ </p>
</li>
<li><p>有两种⽅式显式关闭gap锁：（除了外键约束和唯⼀性检查外，其余情况仅使⽤record lock） A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1</p>
</li>
</ol>
<h2 id="三、关⼼过业务系统⾥⾯的sql耗时吗？统计过慢查询吗？对慢查询都-怎么优化过？"><a href="#三、关⼼过业务系统⾥⾯的sql耗时吗？统计过慢查询吗？对慢查询都-怎么优化过？" class="headerlink" title="三、关⼼过业务系统⾥⾯的sql耗时吗？统计过慢查询吗？对慢查询都 怎么优化过？"></a>三、关⼼过业务系统⾥⾯的sql耗时吗？统计过慢查询吗？对慢查询都 怎么优化过？</h2><p>在业务系统中，除了使⽤主键进⾏的查询，其他的都会在测试库上测试其耗时，慢查询的统计主要由运 维在做，会定期将业务中的慢查询反馈给我们。 </p>
<p>慢查询的优化⾸先要搞明⽩慢的原因是什么？是查询条件没有命中索引？是load了不需要的数据列？还 是数据量太⼤？ </p>
<p>所以优化也是针对这三个⽅向来的， </p>
<ul>
<li><p>⾸先分析语句，看看是否load了额外的数据，可能是查询了多余的⾏并且抛弃掉了，可能是加载了 许多结果中并不需要的列，对语句进⾏分析以及重写。 </p>
</li>
<li><p>分析语句的执⾏计划，然后获得其使⽤索引的情况，之后修改语句或者修改索引，使得语句可以尽 可能的命中索引。 </p>
</li>
<li><p>如果对语句的优化已经⽆法进⾏，可以考虑表中的数据量是否太⼤，如果是的话可以进⾏横向或者 纵向的分表。</p>
</li>
</ul>
<h2 id="四、事务的基本特性和隔离级别"><a href="#四、事务的基本特性和隔离级别" class="headerlink" title="四、事务的基本特性和隔离级别"></a>四、事务的基本特性和隔离级别</h2><p>事务基本特性ACID分别是：</p>
<p><strong>原⼦性</strong>指的是⼀个事务中的操作要么全部成功，要么全部失败。 </p>
<p><strong>⼀致性</strong>指的是数据库总是从⼀个⼀致性的状态转换到另外⼀个⼀致性的状态。⽐如A转账给B100块钱， 假设A只有90块，⽀付之前我们数据库⾥的数据都是符合约束的,但是如果事务执⾏成功了,我们的数据库 数据就破坏约束了,因此事务不能成功,这⾥我们说事务提供了⼀致性的保证 </p>
<p><strong>隔离性</strong>指的是⼀个事务的修改在最终提交前，对其他事务是不可⻅的。 </p>
<p><strong>持久性</strong>指的是⼀旦事务提交，所做的修改就会永久保存到数据库中。 </p>
<p>隔离性有4个隔离级别，分别是： </p>
<ul>
<li><p>read uncommit 读未提交，可能会读到其他事务未提交的数据，也叫做脏读。⽤户本来应该读取到id=1的⽤户age应该是10，结果读取到了其他事务还没有提交的事务，结果读取结果age=20，这就是脏读。 </p>
</li>
<li><p>read commit 读已提交，两次读取结果不⼀致，叫做不可重复读。不可重复读解决了脏读的问题，他只会读取已经提交的事务。⽤户开启事务读取id=1⽤户，查询到age=10，再次读取发现结果=20，在同⼀个事务⾥同⼀个查询读取到不同的结果叫做不可重复读。 </p>
</li>
<li><p>repeatable read 可重复复读，这是mysql的默认级别，就是每次读取结果都⼀样，但是有可能产 ⽣幻读。 </p>
</li>
<li><p>serializable 串⾏，⼀般是不会使⽤的，他会给每⼀⾏读取的数据加锁，会导致⼤量超时和锁竞争 的问题。</p>
</li>
</ul>
<p>脏读(Drity Read)：某个事务已更新⼀份数据，另⼀个事务在此时读取了同⼀份数据，由于某些原因， 前⼀个RollBack了操作，则后⼀个事务所读取的数据就会是不正确的。 </p>
<p>不可重复读(Non-repeatable read):在⼀个事务的两次查询之中数据不⼀致，这可能是两次查询过程中 间插⼊了⼀个事务更新的原有的数据。 </p>
<p>幻读(Phantom Read):在⼀个事务的两次查询中数据笔数不⼀致，例如有⼀个事务查询了⼏列(Row)数 据，⽽另⼀个事务却在此时插⼊了新的⼏列数据，先前的事务在接下来的查询中，就会发现有⼏列数据 是它先前所没有的。 </p>
<h2 id="五、ACID靠什么保证的？"><a href="#五、ACID靠什么保证的？" class="headerlink" title="五、ACID靠什么保证的？"></a>五、ACID靠什么保证的？</h2><p>A原⼦性由undo log⽇志保证，它记录了需要回滚的⽇志信息，事务回滚时撤销已经执⾏成功的sql </p>
<p>C⼀致性由其他三⼤特性保证、程序代码要保证业务上的⼀致性 </p>
<p>I隔离性由MVCC来保证 </p>
<p>D持久性由内存+redo log来保证，mysql修改数据同时在内存和redo log记录这次操作，宕机的时候可 以从redo log恢复 </p>
<blockquote>
<p>InnoDB redo log 写盘，InnoDB 事务进⼊ prepare 状态。 </p>
<p>如果前⾯ prepare 成功，binlog 写盘，再继续将事务⽇志持久化到 binlog，如果持久化成功，那 </p>
<p>么 InnoDB 事务则进⼊ commit 状态(在 redo log ⾥⾯写⼀个 commit 记录) </p>
</blockquote>
<p>redolog的刷盘会在系统空闲时进⾏ </p>
<h2 id="六、什么是MVCC"><a href="#六、什么是MVCC" class="headerlink" title="六、什么是MVCC"></a>六、什么是MVCC</h2><p><strong>多版本并发控制：</strong>读取数据时通过⼀种类似快照的⽅式将数据保存下来，这样读锁就和写锁不冲突了， 不同的事务session会看到⾃⼰特定版本的数据，版本链 </p>
<p>MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下⼯作。其他两个隔离级别够 和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据⾏, ⽽不是符合当前事务版本的数据 ⾏。⽽ SERIALIZABLE 则会对所有读取的⾏都加锁。 </p>
<p>聚簇索引记录中有两个必要的隐藏列： </p>
<p>trx_id：⽤来存储每次对某条聚簇索引记录进⾏修改的时候的事务id。 </p>
<p>roll_pointer：每次对哪条聚簇索引记录有修改的时候，都会把⽼版本写⼊undo⽇志中。这个 roll_pointer就是存了⼀个指针，它指向这条聚簇索引记录的上⼀个版本的位置，通过它来获得上⼀个版 本的记录信息。(注意插⼊操作的undo⽇志没有这个属性，因为它没有⽼版本) </p>
<p>已提交读和可重复读的区别就在于它们⽣成ReadView的策略不同。 </p>
<p>开始事务时创建readview，readView维护当前活动的事务id，即未提交的事务id，排序⽣成⼀个数组 </p>
<p>访问数据，获取数据中的事务id（获取的是事务id最⼤的记录），对⽐readview： </p>
<p>如果在readview的左边（⽐readview都⼩），可以访问（在左边意味着该事务已经提交） </p>
<p>如果在readview的右边（⽐readview都⼤）或者就在readview中，不可以访问，获取roll_pointer，取 上⼀版本重新对⽐（在右边意味着，该事务在readview⽣成之后出现，在readview中意味着该事务还未 提交） </p>
<p>已提交读隔离级别下的事务在每次查询的开始都会⽣成⼀个独⽴的ReadView,⽽可重复读隔离级别则在 第⼀次读的时候⽣成⼀个ReadView，之后的读都复⽤之前的ReadView。 </p>
<p>这就是Mysql的MVCC,通过版本链，实现多版本，可并发读-写，写-读。通过ReadView⽣成策略的不 同实现不同的隔离级别</p>
<h3 id="MVCC实现原理"><a href="#MVCC实现原理" class="headerlink" title="MVCC实现原理"></a>MVCC实现原理</h3><p>对于 InnoDB ，聚簇索引记录中包含 3 个隐藏的列：</p>
<ul>
<li>ROW ID：隐藏的自增 ID，如果表没有主键，InnoDB 会自动按 ROW ID 产生一个聚集索引树。</li>
<li>事务 ID：记录最后一次修改该记录的事务 ID。</li>
<li>回滚指针：指向这条记录的上一个版本。</li>
</ul>
<p>我们拿上面的例子，对应解释下 MVCC 的实现原理，如下图：</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/modb_95751916-225c-11eb-b0bb-5254001c05fe.png" alt="img"></p>
<p>如图，首先 insert 语句向表 t1 中插入了一条数据，a 字段为 1，b 字段为 1， ROW ID 也为 1 ，事务 ID 假设为 1，回滚指针假设为 null。当执行 update t1 set b=666 where a=1 时，大致步骤如下：</p>
<ul>
<li>数据库会先对满足 a=1 的行加排他锁；</li>
<li>然后将原记录复制到 undo 表空间中；</li>
<li>修改 b 字段的值为 666，修改事务 ID 为 2；</li>
<li>并通过隐藏的回滚指针指向 undo log 中的历史记录；</li>
<li>事务提交，释放前面对满足 a=1 的行所加的排他锁。</li>
</ul>
<p>在前面实验的第 6 步中，session2 查询的结果是 session1 修改之前的记录，这个记录就是<strong>来自 undolog</strong> 中。</p>
<p>因此可以总结出 MVCC 实现的原理大致是：</p>
<p>InnoDB 每一行数据都有一个隐藏的回滚指针，用于指向该行修改前的最后一个历史版本，这个历史版本存放在 undo log 中。如果要执行更新操作，会将原记录放入 undo log 中，并通过隐藏的回滚指针指向 undo log 中的原记录。其它事务此时需要查询时，就是查询 undo log 中这行数据的最后一个历史版本。</p>
<p>MVCC 最大的好处是读不加锁，读写不冲突，极大地增加了 MySQL 的并发性。通过 MVCC，保证了事务 ACID 中的 I（隔离性）特性。</p>
<h2 id="七、分表后⾮sharding-key的查询怎么处理，分表后的排序？"><a href="#七、分表后⾮sharding-key的查询怎么处理，分表后的排序？" class="headerlink" title="七、分表后⾮sharding_key的查询怎么处理，分表后的排序？"></a>七、分表后⾮sharding_key的查询怎么处理，分表后的排序？</h2><ol>
<li><p>可以做⼀个mapping表，⽐如这时候商家要查询订单列表怎么办呢？不带user_id查询的话你总不能 扫全表吧？所以我们可以做⼀个映射关系表，保存商家和⽤户的关系，查询的时候先通过商家查询 到⽤户列表，再通过user_id去查询。 </p>
</li>
<li><p>宽表，对数据实时性要求不是很⾼的场景，⽐如查询订单列表，可以把订单表同步到离线（实时） 数仓，再基于数仓去做成⼀张宽表，再基于其他如es提供查询服务。 </p>
</li>
<li><p>数据量不是很⼤的话，⽐如后台的⼀些查询之类的，也可以通过多线程扫表，然后再聚合结果的⽅ 式来做。或者异步的形式也是可以的。</p>
</li>
</ol>
<p>union </p>
<p>排序字段是唯⼀索引： </p>
<ul>
<li>⾸先第⼀⻚的查询：将各表的结果集进⾏合并，然后再次排序 </li>
<li>第⼆⻚及以后的查询，需要传⼊上⼀⻚排序字段的最后⼀个值，及排序⽅式。 </li>
<li>根据排序⽅式，及这个值进⾏查询。如排序字段date，上⼀⻚最后值为3，排序⽅式降序。查询的 时候sql为select … from table where date &lt; 3 order by date desc limit 0,10。这样再将⼏个表的结果合并排序即可。</li>
</ul>
<h2 id="八、Mysql主从同步原理"><a href="#八、Mysql主从同步原理" class="headerlink" title="八、Mysql主从同步原理"></a>八、Mysql主从同步原理</h2><p>mysql主从同步的过程： </p>
<p>Mysql的主从复制中主要有三个线程： master（binlog dump thread）、slave（I/O thread 、SQL thread） ，Master⼀条线程和Slave中的两条线程。 </p>
<ul>
<li><p>主节点 binlog，主从复制的基础是主库记录数据库的所有变更记录到 binlog。binlog 是数据库服 </p>
<p>务器启动的那⼀刻起，保存所有修改数据库结构或内容的⼀个⽂件。 </p>
</li>
<li><p>主节点 log dump 线程，当 binlog 有变动时，log dump 线程读取其内容并发送给从节点。 </p>
</li>
<li><p>从节点 I/O线程接收 binlog 内容，并将其写⼊到 relay log ⽂件中。 </p>
</li>
<li><p>从节点的SQL 线程读取 relay log ⽂件内容对数据更新进⾏重放，最终保证主从数据库的⼀致性。</p>
</li>
</ul>
<p>注：主从节点使⽤ binglog ⽂件 + position 偏移量来定位主从同步的位置，从节点会保存其已接收到的 偏移量，如果从节点发⽣宕机重启，则会⾃动从 position 的位置发起同步。 </p>
<p>由于mysql默认的复制⽅式是异步的，主库把⽇志发送给从库后不关⼼从库是否已经处理，这样会产⽣ ⼀个问题就是假设主库挂了，从库处理失败了，这时候从库升为主库后，⽇志就丢失了。由此产⽣两个 概念。 </p>
<p><strong>全同步复制</strong> </p>
<p>主库写⼊binlog后强制同步⽇志到从库，所有的从库都执⾏完成后才返回给客户端，但是很显然这个⽅ 式的话性能会受到严重影响。 </p>
<p><strong>半同步复制</strong> </p>
<p>和全同步不同的是，半同步复制的逻辑是这样，从库写⼊⽇志成功后返回ACK确认给主库，主库收到⾄ 少⼀个从库的确认就认为写操作完成</p>
<h2 id="九、简述MyISAM和InnoDB的区别"><a href="#九、简述MyISAM和InnoDB的区别" class="headerlink" title="九、简述MyISAM和InnoDB的区别"></a>九、简述MyISAM和InnoDB的区别</h2><p>MyISAM： </p>
<ul>
<li><p>不⽀持事务，但是每次查询都是原⼦的； </p>
</li>
<li><p>⽀持表级锁，即每次操作是对整个表加锁； </p>
</li>
<li><p>存储表的总⾏数； </p>
</li>
<li><p>⼀个MYISAM表有三个⽂件：索引⽂件、表结构⽂件、数据⽂件； </p>
</li>
<li><p>采⽤⾮聚集索引，索引⽂件的数据域存储指向数据⽂件的指针。辅索引与主索引基本⼀致，但是辅 </p>
<p>索引不⽤保证唯⼀性。</p>
</li>
</ul>
<p>InnoDb： </p>
<ul>
<li><p>⽀持ACID的事务，⽀持事务的四种隔离级别； </p>
</li>
<li><p>⽀持⾏级锁及外键约束：因此可以⽀持写并发； </p>
</li>
<li><p>不存储总⾏数； </p>
</li>
<li><p>⼀个InnoDb引擎存储在⼀个⽂件空间（共享表空间，表⼤⼩不受操作系统控制，⼀个表可能分布在 </p>
<p>多个⽂件⾥），也有可能为多个（设置为独⽴表空，表⼤⼩受操作系统⽂件⼤⼩限制，⼀般为 </p>
<p>2G），受操作系统⽂件⼤⼩的限制； </p>
</li>
<li><p>主键索引采⽤聚集索引（索引的数据域存储数据⽂件本身），辅索引的数据域存储主键的值；因此 </p>
<p>从辅索引查找数据，需要先通过辅索引找到主键值，再访问辅索引；最好使⽤⾃增主键，防⽌插⼊ </p>
<p>数据时，为维持B+树结构，⽂件的⼤调整。</p>
</li>
</ul>
<h2 id="十、Explain语句结果中各个字段分表表示什么"><a href="#十、Explain语句结果中各个字段分表表示什么" class="headerlink" title="十、Explain语句结果中各个字段分表表示什么"></a>十、Explain语句结果中各个字段分表表示什么</h2><table>
<thead>
<tr>
<th>列名</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>id</td>
<td>查询语句中每出现⼀个SELECT关键字，MySQL 就会为它分配⼀个唯⼀的id值，某些⼦查询会被 优化为join查询，那么出现的id会⼀样</td>
</tr>
<tr>
<td>select_type</td>
<td>SELECT关键字对应的那个查询的类型</td>
</tr>
<tr>
<td>table</td>
<td>表名</td>
</tr>
<tr>
<td>partitions</td>
<td>匹配的分区信息</td>
</tr>
<tr>
<td>type</td>
<td>针对单表的查询⽅式（全表扫描、索引）</td>
</tr>
<tr>
<td>possible_keys</td>
<td>可能⽤到的索引</td>
</tr>
<tr>
<td>key</td>
<td>实际上使⽤的索引</td>
</tr>
<tr>
<td>key_len</td>
<td>实际使⽤到的索引⻓度</td>
</tr>
<tr>
<td>ref</td>
<td>当使⽤索引列等值查询时，与索引列进⾏等值匹 配的对象信息</td>
</tr>
<tr>
<td>rows</td>
<td>预估的需要读取的记录条数</td>
</tr>
<tr>
<td>filtered</td>
<td>某个表经过搜索条件过滤后剩余记录条数的百分 ⽐</td>
</tr>
<tr>
<td>Extra</td>
<td>⼀些额外的信息，⽐如排序等</td>
</tr>
</tbody></table>
<h2 id="十一、Innodb是如何实现事务的"><a href="#十一、Innodb是如何实现事务的" class="headerlink" title="十一、Innodb是如何实现事务的"></a>十一、Innodb是如何实现事务的</h2><p>Innodb通过Buffer Pool，LogBuffer，Redo Log，Undo Log来实现事务，以⼀个update语句为例： </p>
<ol>
<li><p>Innodb在收到⼀个update语句后，会先根据条件找到数据所在的⻚，并将该⻚缓存在Buffer Pool 中 </p>
</li>
<li><p>执⾏update语句，修改Buffer Pool中的数据，也就是内存中的数据 </p>
</li>
<li><p>针对update语句⽣成⼀个RedoLog对象，并存⼊LogBuffer中 </p>
</li>
<li><p>针对update语句⽣成undolog⽇志，⽤于事务回滚 </p>
</li>
<li><p>如果事务提交，那么则把RedoLog对象进⾏持久化，后续还有其他机制将Buffer Pool中所修改的 数据⻚持久化到磁盘中 </p>
</li>
<li><p>如果事务回滚，则利⽤undolog⽇志进⾏回滚</p>
</li>
</ol>
<h2 id="十二、B树和B-树的区别，为什么Mysql使⽤B-树"><a href="#十二、B树和B-树的区别，为什么Mysql使⽤B-树" class="headerlink" title="十二、B树和B+树的区别，为什么Mysql使⽤B+树"></a>十二、B树和B+树的区别，为什么Mysql使⽤B+树</h2><p><strong>B树的特点：</strong> </p>
<ol>
<li><p>节点排序 </p>
</li>
<li><p>⼀个节点了可以存多个元素，多个元素也排序了</p>
</li>
</ol>
<p><strong>B+树的特点：</strong> </p>
<ol>
<li><p>拥有B树的特点 </p>
</li>
<li><p>叶⼦节点之间有指针 </p>
</li>
<li><p>⾮叶⼦节点上的元素在叶⼦节点上都冗余了，也就是叶⼦节点中存储了所有的元素，并且排好顺序</p>
</li>
</ol>
<p>Mysql索引使⽤的是B+树，因为索引是⽤来加快查询的，⽽B+树通过对数据进⾏排序所以是可以提⾼查 询速度的，然后通过⼀个节点中可以存储多个元素，从⽽可以使得B+树的⾼度不会太⾼，在Mysql中⼀个Innodb⻚就是⼀个B+树节点，⼀个Innodb⻚默认16kb，所以⼀般情况下⼀颗两层的B+树可以存2000万⾏左右的数据，然后通过利⽤B+树叶⼦节点存储了所有数据并且进⾏了排序，并且叶⼦节点之间有指针，可以很好的⽀持全表扫描，范围查找等SQL语句</p>
<h2 id="十三、Mysql锁有哪些，如何理解"><a href="#十三、Mysql锁有哪些，如何理解" class="headerlink" title="十三、Mysql锁有哪些，如何理解"></a>十三、Mysql锁有哪些，如何理解</h2><p><strong>按锁粒度分类：</strong> </p>
<ol>
<li><p>⾏锁：锁某⾏数据，锁粒度最⼩，并发度⾼ </p>
</li>
<li><p>表锁：锁整张表，锁粒度最⼤，并发度低 </p>
</li>
<li><p>间隙锁：锁的是⼀个区间</p>
</li>
</ol>
<p><strong>还可以分为：</strong> </p>
<ol>
<li><p>共享锁：也就是读锁，⼀个事务给某⾏数据加了读锁，其他事务也可以读，但是不能写 </p>
</li>
<li><p>排它锁：也就是写锁，⼀个事务给某⾏数据加了写锁，其他事务不能读，也不能写</p>
</li>
</ol>
<p><strong>还可以分为：</strong> </p>
<ol>
<li><p>乐观锁：并不会真正的去锁某⾏记录，⽽是通过⼀个版本号来实现的 </p>
</li>
<li><p>悲观锁：上⾯所的⾏锁、表锁等都是悲观锁</p>
</li>
</ol>
<p>在事务的隔离级别实现中，就需要利⽤锁来解决幻读 </p>
<h2 id="十四、Mysql慢查询该如何优化？"><a href="#十四、Mysql慢查询该如何优化？" class="headerlink" title="十四、Mysql慢查询该如何优化？"></a>十四、Mysql慢查询该如何优化？</h2><h3 id="1、Mysql执行查询的过程"><a href="#1、Mysql执行查询的过程" class="headerlink" title="1、Mysql执行查询的过程"></a>1、Mysql执行查询的过程</h3><ol>
<li>客户端通过 TCP 连接发送连接请求到 MySQL 连接器，连接器会对该请求进行权限验证及连接资源分配</li>
<li>查缓存。（当判断缓存是否命中时，MySQL 不会进行解析查询语句，而是直接使用 SQL 语句和客户端发送过来的其他原始信息。所以，任何字符上的不同，例如空格、注解等都会导致缓存的不命中。）</li>
<li>语法分析（SQL 语法是否写错了）。 如何把语句给到预处理器，检查数据表和数据列是否存在，解析别名看是否存在歧义。</li>
<li>优化。是否使用索引，生成执行计划。</li>
<li>交给执行器，将数据保存到结果集中，同时会逐步将数据缓存到查询缓存中，最终将结果集返回给客户端。</li>
</ol>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220906211526011.png" alt="image-20220906211526011"></p>
<h3 id="2、慢查询优化"><a href="#2、慢查询优化" class="headerlink" title="2、慢查询优化"></a>2、慢查询优化</h3><ol>
<li>检查是否⾛了索引，如果没有则优化SQL利⽤索引 </li>
<li>检查所利⽤的索引，是否是最优索引 </li>
<li>检查所查字段是否都是必须的，是否查询了过多字段，查出了多余数据 </li>
<li>检查表中数据是否过多，是否应该进⾏分库分表了 </li>
<li>检查数据库实例所在机器的性能配置，是否太低，是否可以适当增加资源</li>
</ol>
<h2 id="十五、常见的几种区别"><a href="#十五、常见的几种区别" class="headerlink" title="十五、常见的几种区别"></a>十五、常见的几种区别</h2><h3 id="1、-MySQL-中的-varchar-和-char-有什么区别？"><a href="#1、-MySQL-中的-varchar-和-char-有什么区别？" class="headerlink" title="1、 MySQL 中的 varchar 和 char 有什么区别？"></a>1、 MySQL 中的 varchar 和 char 有什么区别？</h3><p>char 是一个定长字段,假如申请了<code>char(10)</code>的空间,那么无论实际存储多少内容.该字段都占用 10 个字符,而 varchar 是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1,最后一个字符存储使用了多长的空间.</p>
<p>在检索效率上来讲,char &gt; varchar,因此在使用中,如果确定某个字段的值的长度,可以使用 char,否则应该尽量使用 varchar.例如存储用户 MD5 加密后的密码,则应该使用 char。</p>
<h3 id="2-MySQL中-in-和-exists-区别"><a href="#2-MySQL中-in-和-exists-区别" class="headerlink" title="2. MySQL中 in 和 exists 区别"></a>2. MySQL中 in 和 exists 区别</h3><p>MySQL中的in语句是把外表和内表作hash 连接，而exists语句是对外表作loop循环，每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高，这种说法其实是不准确的。这个是要区分环境的。</p>
<p>如果查询的两个表大小相当，那么用in和exists差别不大。 如果两个表中一个较小，一个是大表，则子查询表大的用exists，子查询表小的用in。 not in 和not exists：如果查询语句使用了not in，那么内外表都进行全表扫描，没有用到索引；而not extsts的子查询依然能用到表上的索引。所以无论那个表大，用not exists都比not in要快。</p>
<h3 id="4-drop、delete与truncate的区别"><a href="#4-drop、delete与truncate的区别" class="headerlink" title="4. drop、delete与truncate的区别"></a>4. drop、delete与truncate的区别</h3><p>三者都表示删除，但是三者有一些差别：</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210822203927822.png" alt="image-20210822203927822"></p>
<h2 id="十六、MySQL中的锁"><a href="#十六、MySQL中的锁" class="headerlink" title="十六、MySQL中的锁"></a>十六、MySQL中的锁</h2><h3 id="1-为什么要加锁"><a href="#1-为什么要加锁" class="headerlink" title="1. 为什么要加锁?"></a>1. 为什么要加锁?</h3><p>当多个用户并发地存取数据时，在<a target="_blank" rel="noopener" href="https://cloud.tencent.com/solution/database?from=10680">数据库</a>中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据，破坏数据库的一致性。</p>
<p>保证多用户环境下保证数据库完整性和一致性。</p>
<h3 id="2-按照锁的粒度分数据库锁有哪些？"><a href="#2-按照锁的粒度分数据库锁有哪些？" class="headerlink" title="2. 按照锁的粒度分数据库锁有哪些？"></a>2. 按照锁的粒度分数据库锁有哪些？</h3><p>在关系型数据库中，可以<strong>按照锁的粒度把数据库锁分</strong>为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )。</p>
<p>行级锁</p>
<ul>
<li>行级锁是<a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/cdb?from=10680">MySQL</a>中锁定粒度最细的一种锁，表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小，但加锁的开销也最大。行级锁分为共享锁 和 排他锁。</li>
<li>开销大，加锁慢；会出现死锁；锁定粒度最小，发生锁冲突的概率最低，并发度也最高。</li>
</ul>
<p>表级锁</p>
<ul>
<li>表级锁是MySQL中锁定粒度最大的一种锁，表示对当前操作的整张表加锁，它实现简单，资源消耗较少，被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁（共享锁）与表独占写锁（排他锁）。</li>
<li>开销小，加锁快；不会出现死锁；锁定粒度大，发出锁冲突的概率最高，并发度最低。</li>
</ul>
<p>页级锁</p>
<ul>
<li>页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快，但冲突多，行级冲突少，但速度慢。所以取了折衷的页级，一次锁定相邻的一组记录。BDB支持页级锁</li>
<li>开销和加锁时间界于表锁和行锁之间；会出现死锁；锁定粒度界于表锁和行锁之间，并发度一般</li>
</ul>
<p><strong>MyISAM和InnoDB存储引擎使用的锁：</strong></p>
<ul>
<li>MyISAM采用表级锁(table-level locking)。</li>
<li>InnoDB支持行级锁(row-level locking)和表级锁，默认为行级锁</li>
</ul>
<h3 id="3-从锁的类别上分MySQL都有哪些锁呢？"><a href="#3-从锁的类别上分MySQL都有哪些锁呢？" class="headerlink" title="3. 从锁的类别上分MySQL都有哪些锁呢？"></a>3. 从锁的类别上分MySQL都有哪些锁呢？</h3><p>从锁的类别上来讲，有共享锁和排他锁。</p>
<ul>
<li>共享锁: 又叫做读锁。 当用户要进行数据的读取时，对数据加上共享锁。共享锁可以同时加上多个。</li>
<li>排他锁: 又叫做写锁。 当用户要进行数据的写入时，对数据加上排他锁。排他锁只可以加一个，他和其他的排他锁，共享锁都相斥。</li>
</ul>
<p>用上面的例子来说就是用户的行为有两种，一种是来看房，多个用户一起看房是可以接受的。 一种是真正的入住一晚，在这期间，无论是想入住的还是想看房的都不可以。</p>
<p>锁的粒度取决于具体的存储引擎，InnoDB实现了行级锁，页级锁，表级锁。</p>
<p>他们的加锁开销从大到小，并发能力也是从大到小。</p>
<h3 id="4-数据库的乐观锁和悲观锁是什么？怎么实现的？"><a href="#4-数据库的乐观锁和悲观锁是什么？怎么实现的？" class="headerlink" title="4. 数据库的乐观锁和悲观锁是什么？怎么实现的？"></a>4. 数据库的乐观锁和悲观锁是什么？怎么实现的？</h3><p>数据库管理系统（DBMS）中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观并发控制（乐观锁）和悲观并发控制（悲观锁）是并发控制主要采用的技术手段。</p>
<ul>
<li>悲观锁：假定会发生并发冲突，屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来，直到提交事务。实现方式：使用数据库中的锁机制</li>
<li>乐观锁：假设不会发生并发冲突，只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来，通过version的方式来进行锁定。实现方式：乐一般会使用版本号机制或CAS算法实现。</li>
</ul>
<p><strong>两种锁的使用场景</strong></p>
<p>从上面对两种锁的介绍，我们知道两种锁各有优缺点，不可认为一种好于另一种，像乐观锁适用于写比较少的情况下（多读场景），即冲突真的很少发生的时候，这样可以省去了锁的开销，加大了系统的整个吞吐量。</p>
<p>但如果是多写的情况，一般会经常产生冲突，这就会导致上层应用会不断的进行retry，这样反倒是降低了性能，所以一般多写的场景下用悲观锁就比较合适。</p>
<h3 id="5-InnoDB引擎的行锁是怎么实现的？"><a href="#5-InnoDB引擎的行锁是怎么实现的？" class="headerlink" title="5. InnoDB引擎的行锁是怎么实现的？"></a>5. InnoDB引擎的行锁是怎么实现的？</h3><p>InnoDB是基于索引来完成行锁</p>
<p>例: select * from tab_with_index where id = 1 for update;</p>
<p>for update 可以根据条件来完成行锁锁定，并且 id 是有索引键的列，如果 id 不是索引键那么InnoDB将完成表锁，并发将无从谈起</p>
<h3 id="6-什么是死锁？怎么解决？"><a href="#6-什么是死锁？怎么解决？" class="headerlink" title="6. 什么是死锁？怎么解决？"></a>6. 什么是死锁？怎么解决？</h3><p>死锁是指两个或多个事务在同一资源上相互占用，并请求锁定对方的资源，从而导致恶性循环的现象。</p>
<p>常见的解决死锁的方法</p>
<p>1、如果不同程序会并发存取多个表，尽量约定以相同的顺序访问表，可以大大降低死锁机会。</p>
<p>2、在同一个事务中，尽可能做到一次锁定所需要的所有资源，减少死锁产生概率；</p>
<p>3、对于非常容易产生死锁的业务部分，可以尝试使用升级锁定颗粒度，通过表级锁定来减少死锁产生的概率；</p>
<p>如果业务处理不好可以用分布式事务锁或者使用乐观锁</p>
<h3 id="7-隔离级别与锁的关系"><a href="#7-隔离级别与锁的关系" class="headerlink" title="7. 隔离级别与锁的关系"></a>7. 隔离级别与锁的关系</h3><p>在Read Uncommitted级别下，读取数据不需要加共享锁，这样就不会跟被修改的数据上的排他锁冲突</p>
<p>在Read Committed级别下，读操作需要加共享锁，但是在语句执行完以后释放共享锁；</p>
<p>在Repeatable Read级别下，读操作需要加共享锁，但是在事务提交之前并不释放共享锁，也就是必须等待事务执行完毕以后才释放共享锁。</p>
<p>SERIALIZABLE 是限制性最强的隔离级别，因为该级别锁定整个范围的键，并一直持有锁，直到事务完成。</p>
<h3 id="8-优化锁方面的意见？"><a href="#8-优化锁方面的意见？" class="headerlink" title="8. 优化锁方面的意见？"></a>8. 优化锁方面的意见？</h3><ul>
<li>使用较低的隔离级别</li>
<li>设计索引，尽量使用索引去访问数据，加锁更加精确，从而减少锁冲突</li>
<li>选择合理的事务大小，给记录显示加锁时，最好一次性请求足够级别的锁。列如，修改数据的话，最好申请排他锁，而不是先申请共享锁，修改时在申请排他锁，这样会导致死锁</li>
<li>不同的程序访问一组表的时候，应尽量约定一个相同的顺序访问各表，对于一个表而言，尽可能的固定顺序的获取表中的行。这样大大的减少死锁的机会。</li>
<li>尽量使用相等条件访问数据，这样可以避免间隙锁对并发插入的影响</li>
<li>不要申请超过实际需要的锁级别</li>
<li>数据查询的时候不是必要，不要使用加锁。MySQL的MVCC可以实现事务中的查询不用加锁，优化事务性能：MVCC只在committed read（读提交）和 repeatable read （可重复读）两种隔离级别</li>
<li>对于特定的事务，可以使用表锁来提高处理速度活着减少死锁的可能。</li>
</ul>
<h2 id="十七、分库分表"><a href="#十七、分库分表" class="headerlink" title="十七、分库分表"></a>十七、分库分表</h2><h3 id="1-为什么要分库分表？"><a href="#1-为什么要分库分表？" class="headerlink" title="1. 为什么要分库分表？"></a>1. 为什么要分库分表？</h3><p><strong>分表</strong></p>
<p>比如你单表都几千万数据了，你确定你能扛住么？绝对不行，单表数据量太大，会极大影响你的 sql执行的性能，到了后面你的 sql 可能就跑的很慢了。一般来说，就以我的经验来看，单表到几百万的时候，性能就会相对差一些了，你就得分表了。</p>
<p>分表就是把一个表的数据放到多个表中，然后查询的时候你就查一个表。比如按照用户 id 来分表，将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内，比如每个表就固定在 200 万以内。</p>
<p><strong>分库</strong></p>
<p>分库就是你一个库一般我们经验而言，最多支撑到并发 2000，一定要扩容了，而且一个健康的单库并发值你最好保持在每秒 1000 左右，不要太大。那么你可以将一个库的数据拆分到多个库中，访问的时候就访问一个库好了。</p>
<p>这就是所谓的分库分表。</p>
<p><img src="https://upload-images.jianshu.io/upload_images/14266602-ae74054f45f44e3d?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="img"></p>
<h3 id="2-用过哪些分库分表中间件？不同的分库分表中间件都有什么优点和缺点？"><a href="#2-用过哪些分库分表中间件？不同的分库分表中间件都有什么优点和缺点？" class="headerlink" title="2. 用过哪些分库分表中间件？不同的分库分表中间件都有什么优点和缺点？"></a>2. 用过哪些分库分表中间件？不同的分库分表中间件都有什么优点和缺点？</h3><p>这个其实就是看看你了解哪些分库分表的中间件，各个中间件的优缺点是啥？然后你用过哪些分库分表的中间件。</p>
<p>比较常见的包括：</p>
<ul>
<li>cobar</li>
<li>TDDL</li>
<li>atlas</li>
<li>sharding-jdbc</li>
<li>mycat</li>
</ul>
<h4 id="cobar"><a href="#cobar" class="headerlink" title="cobar"></a><a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/doc/MySQL/%E9%9D%A2%E8%AF%95%E9%A2%98?id=cobar">cobar</a></h4><p>阿里 b2b 团队开发和开源的，属于 proxy 层方案。早些年还可以用，但是最近几年都没更新了，基本没啥人用，差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库 join 和分页等操作。</p>
<h4 id="TDDL"><a href="#TDDL" class="headerlink" title="TDDL"></a><a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/doc/MySQL/%E9%9D%A2%E8%AF%95%E9%A2%98?id=tddl">TDDL</a></h4><p>淘宝团队开发的，属于 client 层方案。支持基本的 crud 语法和读写分离，但不支持 join、多表查询等语法。目前使用的也不多，因为还依赖淘宝的 diamond 配置管理系统。</p>
<h4 id="atlas"><a href="#atlas" class="headerlink" title="atlas"></a><a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/doc/MySQL/%E9%9D%A2%E8%AF%95%E9%A2%98?id=atlas">atlas</a></h4><p>360 开源的，属于 proxy 层方案，以前是有一些公司在用的，但是确实有一个很大的问题就是社区最新的维护都在 5 年前了。所以，现在用的公司基本也很少了。</p>
<h4 id="sharding-jdbc"><a href="#sharding-jdbc" class="headerlink" title="sharding-jdbc"></a><a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/doc/MySQL/%E9%9D%A2%E8%AF%95%E9%A2%98?id=sharding-jdbc">sharding-jdbc</a></h4><p>当当开源的，属于 client 层方案。确实之前用的还比较多一些，因为 SQL 语法支持也比较多，没有太多限制，而且目前推出到了 2.0 版本，支持分库分表、读写分离、分布式 id 生成、柔性事务（最大努力送达型事务、TCC 事务）。而且确实之前使用的公司会比较多一些（这个在官网有登记使用的公司，可以看到从 2017 年一直到现在，是有不少公司在用的），目前社区也还一直在开发和维护，还算是比较活跃，个人认为算是一个现在也<strong>可以选择的方案</strong>。</p>
<h4 id="mycat"><a href="#mycat" class="headerlink" title="mycat"></a><a target="_blank" rel="noopener" href="https://www.javalearn.cn/#/doc/MySQL/%E9%9D%A2%E8%AF%95%E9%A2%98?id=mycat">mycat</a></h4><p>基于 cobar 改造的，属于 proxy 层方案，支持的功能非常完善，而且目前应该是非常火的而且不断流行的数据库中间件，社区很活跃，也有一些公司开始在用了。但是确实相比于 sharding jdbc 来说，年轻一些，经历的锤炼少一些。</p>
<h3 id="3-如何对数据库如何进行垂直拆分或水平拆分的？"><a href="#3-如何对数据库如何进行垂直拆分或水平拆分的？" class="headerlink" title="3. 如何对数据库如何进行垂直拆分或水平拆分的？"></a>3. 如何对数据库如何进行垂直拆分或水平拆分的？</h3><p><strong>水平拆分</strong>的意思，就是把一个表的数据给弄到多个库的多个表里去，但是每个库的表结构都一样，只不过每个库表放的数据是不同的，所有库表的数据加起来就是全部数据。水平拆分的意义，就是将数据均匀放更多的库里，然后用多个库来抗更高的并发，还有就是用多个库的存储容量来进行扩容。</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/10089464-0e01dfe246b5c7ac.png" alt="img"></p>
<p><strong>垂直拆分</strong>的意思，就是<strong>把一个有很多字段的表给拆分成多个表</strong>，<strong>或者是多个库上去</strong>。每个库表的结构都不一样，每个库表都包含部分字段。一般来说，会<strong>将较少的访问频率很高的字段放到一个表里去</strong>，然后<strong>将较多的访问频率很低的字段放到另外一个表里去</strong>。因为数据库是有缓存的，你访问频率高的行字段越少，就可以在缓存里缓存更多的行，性能就越好。这个一般在表层面做的较多一些。</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/10089464-ab3069913c0f097c.png" alt="img"></p>
<p>两种<strong>分库分表的方式</strong>：</p>
<ul>
<li>一种是按照 range 来分，就是每个库一段连续的数据，这个一般是按比如<strong>时间范围</strong>来的，但是这种一般较少用，因为很容易产生热点问题，大量的流量都打在最新的数据上了。</li>
<li>或者是按照某个字段hash一下均匀分散，这个较为常用。</li>
</ul>
<p>range 来分，好处在于说，扩容的时候很简单，因为你只要预备好，给每个月都准备一个库就可以了，到了一个新的月份的时候，自然而然，就会写新的库了；缺点，但是大部分的请求，都是访问最新的数据。实际生产用 range，要看场景。</p>
<p>hash 分发，好处在于说，可以平均分配每个库的数据量和请求压力；坏处在于说扩容起来比较麻烦，会有一个数据迁移的过程，之前的数据需要重新计算 hash 值重新分配到不同的库或表</p>
<h2 id="十八、读写分离、主从同步（复制）"><a href="#十八、读写分离、主从同步（复制）" class="headerlink" title="十八、读写分离、主从同步（复制）"></a>十八、读写分离、主从同步（复制）</h2><h3 id="1-什么是MySQL主从同步？"><a href="#1-什么是MySQL主从同步？" class="headerlink" title="1. 什么是MySQL主从同步？"></a>1. 什么是MySQL主从同步？</h3><p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220910090750024.png" alt="image-20220910090750024"></p>
<h4 id="（1）配置主库Master"><a href="#（1）配置主库Master" class="headerlink" title="（1）配置主库Master"></a>（1）配置主库Master</h4><p>修改mysql数据库的配置文件 /etc/my.cnf</p>
<pre><code>[mysqld]
log-bin=mysql-bin #必须启用二进制日志
server-id=100 #服务器唯一ID
</code></pre>
<h4 id=""><a href="#" class="headerlink" title=""></a><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220910091641002.png" alt="image-20220910091641002"></h4><h4 id="（2）配置从库"><a href="#（2）配置从库" class="headerlink" title="（2）配置从库"></a>（2）配置从库</h4><p>修改mysql数据库配置文件/etc/my.cnf</p>
<pre><code>[mysqld]
server-id=101 #设置服务器唯一id
</code></pre>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220910092251829.png" alt="image-20220910092251829"></p>
<h3 id="2-MySQL主从同步的目的？为什么要做主从同步？"><a href="#2-MySQL主从同步的目的？为什么要做主从同步？" class="headerlink" title="2. MySQL主从同步的目的？为什么要做主从同步？"></a>2. MySQL主从同步的目的？为什么要做主从同步？</h3><ol>
<li>通过增加从服务器来提高数据库的性能，在主服务器上执行写入和更新，在从服务器上向外提供读功能，可以动态地调整从服务器的数量，从而调整整个数据库的性能。</li>
<li>提高数据安全-因为数据已复制到从服务器，从服务器可以终止复制进程，所以，可以在从服务器上备份而不破坏主服务器相应数据</li>
<li>在主服务器上生成实时数据，而在从服务器上分析这些数据，从而提高主服务器的性能</li>
<li>数据备份。一般我们都会做数据备份，可能是写定时任务，一些特殊行业可能还需要手动备份，有些行业要求备份和原数据不能在同一个地方，所以主从就能很好的解决这个问题，不仅备份及时，而且还可以多地备份，保证数据的安全</li>
</ol>
<h3 id="3-如何实现MySQL的读写分离？"><a href="#3-如何实现MySQL的读写分离？" class="headerlink" title="3. 如何实现MySQL的读写分离？"></a>3. 如何实现MySQL的读写分离？</h3><p>其实很简单，就是基于主从复制架构，简单来说，就搞一个主库，挂多个从库，然后我们就单单只是写主库，然后主库会自动把数据给同步到从库上去。</p>
<h3 id="4-MySQL主从复制流程和原理？"><a href="#4-MySQL主从复制流程和原理？" class="headerlink" title="4. MySQL主从复制流程和原理？"></a>4. MySQL主从复制流程和原理？</h3><p>基本原理流程，是3个线程以及之间的关联</p>
<p>主：binlog线程——记录下所有改变了数据库数据的语句，放进master上的binlog中；</p>
<p>从：io线程——在使用start slave 之后，负责从master上拉取 binlog 内容，放进自己的relay log中；</p>
<p>从：sql执行线程——执行relay log中的语句；</p>
<p><strong>复制过程如下</strong>：</p>
<p><img src="http://blog-img.coolsen.cn/img/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOC85LzIxLzE2NWZiNjgzMjIyMDViMmU" alt="img"></p>
<p>Binary log：主数据库的二进制日志</p>
<p>Relay log：从服务器的中继日志</p>
<p>第一步：master在每个事务更新数据完成之前，将该操作记录串行地写入到binlog文件中。</p>
<p>第二步：salve开启一个I/O Thread，该线程在master打开一个普通连接，主要工作是binlog dump process。如果读取的进度已经跟上了master，就进入睡眠状态并等待master产生新的事件。I/O线程最终的目的是将这些事件写入到中继日志中。</p>
<p>第三步：SQL Thread会读取中继日志，并顺序执行该日志中的SQL事件，从而与主数据库中的数据保持一致。</p>
<h3 id="5-MySQL主从同步延时问题如何解决？"><a href="#5-MySQL主从同步延时问题如何解决？" class="headerlink" title="5. MySQL主从同步延时问题如何解决？"></a>5. MySQL主从同步延时问题如何解决？</h3><p>MySQL 实际上在有两个同步机制，一个是半同步复制，用来 解决主库数据丢失问题；一个是并行复制，用来 解决主从同步延时问题。</p>
<ul>
<li>半同步复制，也叫 semi-sync 复制，指的就是主库写入 binlog 日志之后，就会将强制此时立即将数据同步到从库，从库将日志写入自己本地的 relay log 之后，接着会返回一个 ack 给主库，主库接收到至少一个从库的 ack 之后才会认为写操作完成了。</li>
<li>并行复制，指的是从库开启多个线程，并行读取 relay log 中不同库的日志，然后并行重放不同库的日志，这是库级别的并行。</li>
</ul>
<h2 id="十九、MySQL优化"><a href="#十九、MySQL优化" class="headerlink" title="十九、MySQL优化"></a>十九、MySQL优化</h2><p>都有哪些维度可以进行数据库调优？简言之：</p>
<ul>
<li>索引失效，没有充分利用到索引——索引建立</li>
<li>关联查询太多JOIN（设计缺陷或不得已的需求）——SQL优化</li>
<li>服务器调优及各个参数设置（缓冲、线程数等）——调整my.cnf3</li>
<li>数据过多——分库分表</li>
</ul>
<p>大致方向上完全可以分成物理查询优化和逻辑查询优化两大块</p>
<p><img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20220909222822091.png" alt="image-20220909222822091"></p>
<h3 id="1-如何定位及优化SQL语句的性能问题？"><a href="#1-如何定位及优化SQL语句的性能问题？" class="headerlink" title="1. 如何定位及优化SQL语句的性能问题？"></a>1. 如何定位及优化SQL语句的性能问题？</h3><p>对于低性能的SQL语句的定位，最重要也是最有效的方法就是使用执行计划，MySQL提供了explain命令来查看语句的执行计划。 我们知道，不管是哪种数据库，或者是哪种数据库引擎，在对一条SQL语句进行执行的过程中都会做很多相关的优化，对于查询语句，最重要的优化方式就是<strong>使用索引</strong>。</p>
<p>而执行计划，就是显示数据库引擎对于SQL语句的执行的详细情况，其中包含了是否使用索引，使用什么索引，使用的索引的相关信息等。 <img src="https://notes-hub.oss-cn-beijing.aliyuncs.com/typora_images/image-20210822204026552.png" alt="image-20210822204026552"></p>
<h3 id="2-大表数据查询，怎么优化"><a href="#2-大表数据查询，怎么优化" class="headerlink" title="2. 大表数据查询，怎么优化"></a>2. 大表数据查询，怎么优化</h3><ul>
<li>优化shema、sql语句+索引；</li>
<li>第二加缓存，memcached, redis；</li>
<li>主从复制，读写分离；</li>
<li>垂直拆分，根据你模块的耦合度，将一个大的系统分为多个小的系统，也就是分布式系统；</li>
<li>水平切分，针对数据量大的表，这一步最麻烦，最能考验技术水平，要选择一个合理的sharding key, 为了有好的查询效率，表结构也要改动，做一定的冗余，应用也要改，sql中尽量带sharding key，将数据定位到限定的表上去查，而不是扫描全部的表；</li>
</ul>
<h3 id="3-超大分页怎么处理"><a href="#3-超大分页怎么处理" class="headerlink" title="3. 超大分页怎么处理?"></a>3. 超大分页怎么处理?</h3><p>数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于<code>select * from table where age &gt; 20 limit 1000000</code>,10 这种查询其实也是有可以优化的余地的. 这条语句需要 load1000000 数据然后基本上全部丢弃,只取 10 条当然比较慢. 当时我们可以修改为<code>select * from table where id in (select id from table where age &gt; 20 limit 1000000,10)</code>.这样虽然也 load 了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快。</p>
<p>解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可.</p>
<p>在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种.</p>
<blockquote>
<p>【推荐】利用延迟关联或者子查询优化超多分页场景。</p>
<p>说明：MySQL并不是跳过offset行，而是取offset+N行，然后返回放弃前offset行，返回N行，那当offset特别大的时候，效率就非常的低下，要么控制返回的总页数，要么对超过特定阈值的页数进行SQL改写。</p>
<p>正例：先快速定位需要获取的id段，然后再关联：</p>
<p>SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id</p>
</blockquote>
<h3 id="4-统计过慢查询吗？对慢查询都怎么优化过？"><a href="#4-统计过慢查询吗？对慢查询都怎么优化过？" class="headerlink" title="4. 统计过慢查询吗？对慢查询都怎么优化过？"></a>4. 统计过慢查询吗？对慢查询都怎么优化过？</h3><p>在业务系统中，除了使用主键进行的查询，其他的我都会在测试库上测试其耗时，慢查询的统计主要由运维在做，会定期将业务中的慢查询反馈给我们。</p>
<p>慢查询的优化首先要搞明白慢的原因是什么？ 是查询条件没有命中索引？是load了不需要的数据列？还是数据量太大？</p>
<p>所以优化也是针对这三个方向来的，</p>
<ul>
<li>首先分析语句，看看是否load了额外的数据，可能是查询了多余的行并且抛弃掉了，可能是加载了许多结果中并不需要的列，对语句进行分析以及重写。</li>
<li>分析语句的执行计划，然后获得其使用索引的情况，之后修改语句或者修改索引，使得语句可以尽可能的命中索引。</li>
<li>如果对语句的优化已经无法进行，可以考虑表中的数据量是否太大，如果是的话可以进行横向或者纵向的分表。</li>
</ul>
<h3 id="5-如何优化查询过程中的数据访问"><a href="#5-如何优化查询过程中的数据访问" class="headerlink" title="5. 如何优化查询过程中的数据访问"></a>5. 如何优化查询过程中的数据访问</h3><ul>
<li>访问数据太多导致查询性能下降</li>
<li>确定应用程序是否在检索大量超过需要的数据，可能是太多行或列</li>
<li>确认MySQL服务器是否在分析大量不必要的数据行</li>
<li>查询不需要的数据。解决办法：使用limit解决</li>
<li>多表关联返回全部列。解决办法：指定列名</li>
<li>总是返回全部列。解决办法：避免使用SELECT *</li>
<li>重复查询相同的数据。解决办法：可以缓存数据，下次直接读取缓存</li>
<li>是否在扫描额外的记录。解决办法： 使用explain进行分析，如果发现查询需要扫描大量的数据，但只返回少数的行，可以通过如下技巧去优化： 使用索引覆盖扫描，把所有的列都放到索引中，这样存储引擎不需要回表获取对应行就可以返回结果。</li>
<li>改变数据库和表的结构，修改数据表范式</li>
<li>重写SQL语句，让优化器可以以更优的方式执行查询。</li>
</ul>
<h3 id="6-如何优化关联查询"><a href="#6-如何优化关联查询" class="headerlink" title="6. 如何优化关联查询"></a>6. 如何优化关联查询</h3><ul>
<li>确定ON或者USING子句中是否有索引。</li>
<li>确保GROUP BY和ORDER BY只有一个表中的列，这样MySQL才有可能使用索引。</li>
</ul>
<h3 id="7-数据库结构优化"><a href="#7-数据库结构优化" class="headerlink" title="7. 数据库结构优化"></a>7. 数据库结构优化</h3><p>一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。</p>
<p>需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。</p>
<ol>
<li><strong>将字段很多的表分解成多个表</strong></li>
</ol>
<p>对于字段较多的表，如果有些字段的使用频率很低，可以将这些字段分离出来形成新表。</p>
<p>因为当一个表的数据量很大时，会由于使用频率低的字段的存在而变慢。</p>
<ol>
<li><strong>增加中间表</strong></li>
</ol>
<p>对于需要经常联合查询的表，可以建立中间表以提高查询效率。</p>
<p>通过建立中间表，将需要通过联合查询的数据插入到中间表中，然后将原来的联合查询改为对中间表的查询。</p>
<ol>
<li><strong>增加冗余字段</strong></li>
</ol>
<p>设计数据表时应尽量遵循范式理论的规约，尽可能的减少冗余字段，让数据库设计看起来精致、优雅。但是，合理的加入冗余字段可以提高查询速度。</p>
<p>表的规范化程度越高，表和表之间的关系越多，需要连接查询的情况也就越多，性能也就越差。</p>
<p>注意：</p>
<p>冗余字段的值在一个表中修改了，就要想办法在其他表中更新，否则就会导致数据不一致的问题。</p>
<h3 id="8-MySQL数据库cpu飙升到500-的话他怎么处理？"><a href="#8-MySQL数据库cpu飙升到500-的话他怎么处理？" class="headerlink" title="8. MySQL数据库cpu飙升到500%的话他怎么处理？"></a>8. MySQL数据库cpu飙升到500%的话他怎么处理？</h3><p>当 cpu 飙升到 500%时，先用操作系统命令 top 命令观察是不是 MySQLd 占用导致的，如果不是，找出占用高的进程，并进行相关处理。</p>
<p>如果是 MySQLd 造成的， show processlist，看看里面跑的 session 情况，是不是有消耗资源的 sql 在运行。找出消耗高的 sql，看看执行计划是否准确， index 是否缺失，或者实在是数据量太大造成。</p>
<p>一般来说，肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降)，等进行相应的调整(比如说加索引、改 sql、改内存参数)之后，再重新跑这些 SQL。</p>
<p>也有可能是每个 sql 消耗资源并不多，但是突然之间，有大量的 session 连进来导致 cpu 飙升，这种情况就需要跟应用一起来分析为何连接数会激增，再做出相应的调整，比如说限制连接数等。</p>
<h3 id="9-大表怎么优化？"><a href="#9-大表怎么优化？" class="headerlink" title="9. 大表怎么优化？"></a>9. 大表怎么优化？</h3><p>类似的问题：某个表有近千万数据，CRUD比较慢，如何优化？分库分表了是怎么做的？分表分库了有什么问题？有用到中间件么？他们的原理知道么？</p>
<p>当MySQL单表记录数过大时，数据库的CRUD性能会明显下降，一些常见的优化措施如下：</p>
<ul>
<li>限定数据的范围： 务必禁止不带任何限制数据范围条件的查询语句。比如：我们当用户在查询订单历史的时候，我们可以控制在一个月的范围内；</li>
<li>读/写分离： 经典的数据库拆分方案，主库负责写，从库负责读；</li>
<li>缓存： 使用MySQL的缓存，另外对重量级、更新少的数据可以考虑；</li>
<li>通过分库分表的方式进行优化，主要有垂直分表和水平分表。</li>
</ul>
<h2 id="二十、数据库连接池"><a href="#二十、数据库连接池" class="headerlink" title="二十、数据库连接池"></a>二十、数据库连接池</h2><p>连接池的作用<br>首先，连接池的作用就是为了提高性能。<br>       连接池的作用：连接池是将已经创建好的连接保存在池中，当有请求来时，直接使用已经创建好的连接对数据库进行访问。这样省略了创建连接和销毁连接的过程。这样性能上得到了提高。</p>
<p>基本原理是这样的：</p>
<p>（1）建立数据库连接池对象（服务器启动）。<br>（2）按照事先指定的参数创建初始数量的数据库连接（即：空闲连接数）。<br>（3）对于一个数据库访问请求，直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接，且连接数没有达到最大（即：最大活跃连接数），创建一个新的数据库连接。<br>（4）存取数据库。<br>（5）关闭数据库，释放所有数据库连接（此时的关闭数据库连接，并非真正关闭，而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接）。<br>（6）释放数据库连接池对象（服务器停止、维护期间，释放数据库连接池对象，并释放所有连接）。</p>
<p>1 .连接池的概念和为什么要使用连接池？<br>    连接池放了N个Connection对象，本质上放在内存当中，在内存中划出一块缓存对象，应用程序每次从池里获得Connection对象，而不是直接从数据里获得，这样不占用服务器的内存资源。</p>
<p>2 .如果不使用连接池会出现的情况：<br>a.占用服务器的内存资源<br>b.导致服务器的速度非常慢</p>
<p>3 .应用连接池的三种方式：<br>a.自定义连接池<br>b.使用第三方连接池<br>c.使用服务器自带的连接池</p>
<pre><code>   连接池一般比直接连接更有优越性,因为它提高了性能的同时还保存了宝贵的资源。在整个应用程序的使用过程,当中重复的打开直接连接将导致性能的下降。而池连接只在服务器启动时打开一次，从而消除了这种性能问题。
    连接池主要考虑的是性能，每次获取连接和释放连接都有很大的工作量，会对性能有很大影响；而对资源来说起的是反作用，因为保存一定数量的连接是要消耗内存的。应用程序每次从池里获得Connection对象，而不是直接从数据里获得，这样不占用服务器的内存资源。所以一般要建立连接池，而连接的数量要适当，不能太大，太大会过多消耗资源。(所以，考虑2个方面，一个是内存，另一个是资源)。

   连接池就是为了避免重复多次的打开数据库连接而造成的性能的下降和系统资源的浪费。
</code></pre>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">YangChao</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://monkey-chao.gitee.io/2022/08/09/java-mian-shi-ti-mysql-pian/">https://monkey-chao.gitee.io/2022/08/09/java-mian-shi-ti-mysql-pian/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">YangChao</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Java/">
                                    <span class="chip bg-color">Java</span>
                                </a>
                            
                                <a href="/tags/%E9%9D%A2%E8%AF%95/">
                                    <span class="chip bg-color">面试</span>
                                </a>
                            
                                <a href="/tags/Mysql/">
                                    <span class="chip bg-color">Mysql</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    
    <div class="livere-card card" data-aos="fade-up">
    <!-- 来必力City版安装代码 -->
    <div id="lv-container" class="card-content" data-id="city" data-uid="">
        <script type="text/javascript">
            (function (d, s) {
                let j, e = d.getElementsByTagName(s)[0];
                if (typeof LivereTower === 'function') {
                    return;
                }

                j = d.createElement(s);
                j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
                j.async = true;

                e.parentNode.insertBefore(j, e);
            })(document, 'script');
        </script>
        <noscript>为正常使用来必力评论功能请激活JavaScript。</noscript>
    </div>
    <!-- City版安装代码已完成 -->
</div>
    

    
        <style>
    .valine-card {
        margin: 1.5rem auto;
    }

    .valine-card .card-content {
        padding: 20px 20px 5px 20px;
    }

    #vcomments textarea {
        box-sizing: border-box;
        background: url("/medias/comment_bg.png") 100% 100% no-repeat;
    }

    #vcomments p {
        margin: 2px 2px 10px;
        font-size: 1.05rem;
        line-height: 1.78rem;
    }

    #vcomments blockquote p {
        text-indent: 0.2rem;
    }

    #vcomments a {
        padding: 0 2px;
        color: #4cbf30;
        font-weight: 500;
        text-decoration: none;
    }

    #vcomments img {
        max-width: 100%;
        height: auto;
        cursor: pointer;
    }

    #vcomments ol li {
        list-style-type: decimal;
    }

    #vcomments ol,
    ul {
        display: block;
        padding-left: 2em;
        word-spacing: 0.05rem;
    }

    #vcomments ul li,
    ol li {
        display: list-item;
        line-height: 1.8rem;
        font-size: 1rem;
    }

    #vcomments ul li {
        list-style-type: disc;
    }

    #vcomments ul ul li {
        list-style-type: circle;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    #vcomments table, th, td {
        border: 0;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments h1 {
        font-size: 1.85rem;
        font-weight: bold;
        line-height: 2.2rem;
    }

    #vcomments h2 {
        font-size: 1.65rem;
        font-weight: bold;
        line-height: 1.9rem;
    }

    #vcomments h3 {
        font-size: 1.45rem;
        font-weight: bold;
        line-height: 1.7rem;
    }

    #vcomments h4 {
        font-size: 1.25rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    #vcomments h5 {
        font-size: 1.1rem;
        font-weight: bold;
        line-height: 1.4rem;
    }

    #vcomments h6 {
        font-size: 1rem;
        line-height: 1.3rem;
    }

    #vcomments p {
        font-size: 1rem;
        line-height: 1.5rem;
    }

    #vcomments hr {
        margin: 12px 0;
        border: 0;
        border-top: 1px solid #ccc;
    }

    #vcomments blockquote {
        margin: 15px 0;
        border-left: 5px solid #42b983;
        padding: 1rem 0.8rem 0.3rem 0.8rem;
        color: #666;
        background-color: rgba(66, 185, 131, .1);
    }

    #vcomments pre {
        font-family: monospace, monospace;
        padding: 1.2em;
        margin: .5em 0;
        background: #272822;
        overflow: auto;
        border-radius: 0.3em;
        tab-size: 4;
    }

    #vcomments code {
        font-family: monospace, monospace;
        padding: 1px 3px;
        font-size: 0.92rem;
        color: #e96900;
        background-color: #f8f8f8;
        border-radius: 2px;
    }

    #vcomments pre code {
        font-family: monospace, monospace;
        padding: 0;
        color: #e8eaf6;
        background-color: #272822;
    }

    #vcomments pre[class*="language-"] {
        padding: 1.2em;
        margin: .5em 0;
    }

    #vcomments code[class*="language-"],
    pre[class*="language-"] {
        color: #e8eaf6;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }

    #vcomments b,
    strong {
        font-weight: bold;
    }

    #vcomments dfn {
        font-style: italic;
    }

    #vcomments small {
        font-size: 85%;
    }

    #vcomments cite {
        font-style: normal;
    }

    #vcomments mark {
        background-color: #fcf8e3;
        padding: .2em;
    }

    #vcomments table, th, td {
        padding: 12px 13px;
        border: 1px solid #dfe2e5;
    }

    table tr:nth-child(2n), thead {
        background-color: #fafafa;
    }

    #vcomments table th {
        background-color: #f2f2f2;
        min-width: 80px;
    }

    #vcomments table td {
        min-width: 80px;
    }

    #vcomments [type="checkbox"]:not(:checked), [type="checkbox"]:checked {
        position: inherit;
        margin-left: -1.3rem;
        margin-right: 0.4rem;
        margin-top: -1px;
        vertical-align: middle;
        left: unset;
        visibility: visible;
    }
</style>

<div class="card valine-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="vcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/valine/av-min.js"></script>
<script src="/libs/valine/Valine.min.js"></script>
<script>
    new Valine({
        el: '#vcomments',
        appId: '1543373971',
        appKey: '111111',
        notify: 'false' === 'true',
        verify: 'false' === 'true',
        visitor: 'true' === 'true',
        avatar: 'mm',
        pageSize: '10',
        lang: 'zh-cn',
        placeholder: 'just go go'
    });
</script>

<!--酷Q推送-->


    

    
        <style>
    .mvaline-card {
        margin: 1.5rem auto;
    }

    .mvaline-card .card-content {
        padding: 20px 20px 5px 20px;
    }
</style>

<div class="card mvaline-card" data-aos="fade-up">
    <div class="comment_headling" style="font-size: 20px; font-weight: 700; position: relative; padding-left: 20px; top: 15px; padding-bottom: 5px;">
        <i class="fas fa-comments fa-fw" aria-hidden="true"></i>
        <span>评论</span>
    </div>
    <div id="mvcomments" class="card-content" style="display: grid">
    </div>
</div>

<script src="/libs/minivaline/MiniValine.js"></script>
<script>
    new MiniValine(Object.assign({"enable":true,"serverURL":"https://minivaline.your-domain.top"}, {
	  el: '#mvcomments',
    }));
</script>

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2022/08/10/java-mian-shi-ti-redis-pian/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/0.jpg" class="responsive-img" alt="Java面试题（Redis篇）">
                        
                        <span class="card-title">Java面试题（Redis篇）</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            Java面试Redis题型相关讲解
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2022-08-10
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                    面试宝典
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Redis/">
                        <span class="chip bg-color">Redis</span>
                    </a>
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E8%AF%95/">
                        <span class="chip bg-color">面试</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2022/08/08/java-mian-shi-ti-kuang-jia-pian/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/1.jpg" class="responsive-img" alt="Java面试题（框架篇）">
                        
                        <span class="card-title">Java面试题（框架篇）</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            Java面试开发框架相关讲解
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2022-08-08
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E9%9D%A2%E8%AF%95%E5%AE%9D%E5%85%B8/" class="post-category">
                                    面试宝典
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                    <a href="/tags/%E9%9D%A2%E8%AF%95/">
                        <span class="chip bg-color">面试</span>
                    </a>
                    
                    <a href="/tags/%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6/">
                        <span class="chip bg-color">开发框架</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE' || selection.getRangeAt(0).commonAncestorContainer.nodeName === 'CODE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: Angus<br />'
            + '文章作者: YangChao<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    

    <div class="container row center-align"
         style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2019-2023</span>
            
            <a href="/about" target="_blank">YangChao</a>
<!--             |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" target="_blank">Matery</a> -->
            <br>
            
                &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                        class="white-color">460.9k</span>
            
            
            
                
            
            
                <span id="busuanzi_container_site_pv">
                &nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;
                    <span id="busuanzi_value_site_pv" class="white-color"></span>
            </span>
            
            
                <span id="busuanzi_container_site_uv">
                &nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;
                    <span id="busuanzi_value_site_uv" class="white-color"></span>
            </span>
            
            <br>

            <!-- 运行天数提醒. -->
            
            <br>
            
        </div>
<!--         <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/Monkey-chao/Monkey-chao.github.io.git" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>




<!-- 

 -->


    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1543373971" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1543373971" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>


<!-- 




    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

 --></div>
 -->     </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    

    
        
        <script type="text/javascript">
            // 只在桌面版网页启用特效
            var windowWidth = $(window).width();
            if (windowWidth > 768) {
                document.write('<script type="text/javascript" src="/libs/others/sakura.js"><\/script>');
            }
        </script>
    

    <!-- 雪花特效 -->
    

    <!-- 鼠标星星特效 -->
    

    

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    <!--腾讯兔小巢-->
    
    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
